package com.bubbo.rpc.client.service;

import com.bubbo.rpc.client.bean.RpcRequest;
import com.bubbo.rpc.client.bean.RpcResponse;
import com.bubbo.rpc.client.util.Decoder;
import com.bubbo.rpc.client.util.Encoder;
import com.bubbo.rpc.client.util.StringUtil;
import com.bubbo.rpc.client.util.serialize.Serializer;
import com.bubbo.rpc.client.util.serialize.impl.ProtostuffSerializer;
import com.bubbo.serviceCenter.ServiceRegister;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;

/**
 * RPC 服务端
 * Created by waasdasd on 2017/3/29.
 */
public class BubboServer implements ApplicationContextAware, InitializingBean {
    private static final Logger LOGGER = LoggerFactory.getLogger(BubboServer.class);

    public BubboServer(String serviceAddress) throws SocketException {
        this.serviceAddress = serviceAddress;
    }

    private String serviceAddress;


    private ServiceRegister serviceRegister;

    public BubboServer(String serviceAddress, ServiceRegister serviceRegister) {
        this.serviceAddress = serviceAddress;
        this.serviceRegister = serviceRegister;
        this.serializer = new ProtostuffSerializer();
    }

    public BubboServer(String serviceAddress, ServiceRegister serviceRegister, Serializer serializer) {
        this.serviceAddress = serviceAddress;
        this.serviceRegister = serviceRegister;
        this.serializer = serializer;
    }

    private Serializer serializer;

    /**
     * 存放 服务名 与 服务对象 之间的映射关系
     */
    private Map<String, Object> handlerMap = new HashMap<>();


    @Override
    public void afterPropertiesSet() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup);
            bootstrap.channel(NioServerSocketChannel.class);
            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline pipeline = ch.pipeline();
                    pipeline.addLast(new Decoder(RpcRequest.class, serializer));
                    pipeline.addLast(new Encoder(RpcResponse.class, serializer));
                    pipeline.addLast(new BubboServiceHandler(handlerMap));
                }
            });
            bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
            bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
            //ip,端口
            String[] addressArray = StringUtil.split(serviceAddress, ":");
            String ip = addressArray[0];
            int port = Integer.parseInt(addressArray[1]);
            //start
            ChannelFuture future = bootstrap.bind(ip, port).sync();
            //register

            if (serviceRegister != null) {
                for (String interfaceName : handlerMap.keySet()) {
                    serviceRegister.register(interfaceName, serviceAddress);
                    LOGGER.debug("register service: {} => {}", interfaceName, serviceAddress);
                }
            }
            future.channel().closeFuture().sync();
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //获取所有注解标签
        Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(BubboService.class);
        if (MapUtils.isNotEmpty(serviceBeanMap)) {
            for (Object serviceBean : serviceBeanMap.values()) {
                BubboService service = serviceBean.getClass().getAnnotation(BubboService.class);
                String serviceName = service.value().getName();
                String serviceValue = service.version();
                if (StringUtil.isNotEmpty(serviceValue)) {
                    serviceName += "-" + serviceValue;
                }
                handlerMap.put(serviceName, serviceBean);
            }
        }
    }
}
